package com.xcity.game.combat.listener.combat;

import java.text.MessageFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;

import com.xcity.Server;
import com.xcity.db.entity.Build;
import com.xcity.db.entity.Employee;
import com.xcity.db.entity.Player;
import com.xcity.game.adventure.AdventureType;
import com.xcity.game.adventure.AdventureUtils;
import com.xcity.game.build.BuildCaptureCallback;
import com.xcity.game.build.BuildService;
import com.xcity.game.buildv2.BuildV2;
import com.xcity.game.buildv2.BuildV2.State;
import com.xcity.game.combat.Combat;
import com.xcity.game.combat.Combat.Type;
import com.xcity.game.combat.CombatException;
import com.xcity.game.combat.CombatService;
import com.xcity.game.combat.Round;
import com.xcity.game.combat.builder.DefaultCombatBuilder;
import com.xcity.game.combat.listener.CombatListener;
import com.xcity.game.common.Cause;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.formation.Formation;
import com.xcity.game.news.NewsService;
import com.xcity.game.npc.NpcTemplate;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.game.player.robot.RobotBuild;
import com.xcity.game.player.robot.RobotUtils;
import com.xcity.game.relation.RelationService;
import com.xcity.game.vip.VipUtils;
import com.xcity.pomelo.push.NoticePush;
import com.xcity.pomelo.push.msg.BeAttackedPush;
import com.xcity.pomelo.push.msg.CombatEndPush;
import com.xcity.pomelo.request.CombatInfoRequest;
import com.xcity.pomelo.request.CombatPVPRequest;
import com.xcity.pomelo.response.CombatInfoResponse;
import com.xcity.pomelo.response.CombatInfoResponse.Team;
import com.xcity.pomelo.response.CombatInfoResponse.TeamBuilder;
import com.xcity.pomelo.response.CombatResponse;

import naga.x.App;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Packet;
import naga.x.net.Session;

public class PVPCombatListenerV2 implements CombatListener, BuildCaptureCallback {

	private Action action;
	private Player source, target;
//	private Build build;
	private BuildV2 build;
	private Result result;
	private boolean fromAdventure;
	private boolean targetIsRobot;
	static final int MAIL_VALID_TIME = 10 * 24 * 60 * 60 * 1000;
	protected static final Logger LOG = CombatService.LOG;
	
	static Map<Long, BuildCaptureCallback> callbacks = new ConcurrentHashMap<>(16);
	private long timeout;
	
	static enum Action {
		INVADE,    // 掠夺别人未被占领的建筑（侵攻战）
		BEAT_BACK, // 夺回被别人占领的建筑（反击战）
		BLOCK      // 掠夺被别人占领的别人的建筑（阻击战）
	}
	
	@Override
	public void prepareCombat(Session session, CombatInfoRequest packet) throws CombatException {
		long targetPlayerId = packet.getPVPTargetPlayerId();
		int targetBuildId = packet.getPVPTargetBuildId();
		validate(session, targetPlayerId, targetBuildId);
		
		TeamBuilder sourceTeamBuilder = new TeamBuilder();//.setLeader(source);//.setCombatPower(CombatUtils.calcCombatPowerWithEmployees(source));
		TeamBuilder targetTeamBuilder = new TeamBuilder();//.setLeader(target);//.setCombatPower(CombatUtils.calcCombatPower(target, eids));
		if (RobotUtils.isRobot(target)) { // 机器人
			targetIsRobot = true;
			// 发动攻击的玩家
//			int[] npcIds = GameStaticConfig.adventureFirstCaptureSrcNpcs;
//			TemplateService ts = App.getApp().getServiceManager().get(TemplateService.class);
//			for (int id : npcIds) {
//				NpcTemplate npc = ts.get(NpcTemplate.class, id);
//				sourceTeamBuilder.addMember(npc);
//			}
			Employee[] srcEmployees = source.getFormations().get(Formation.TYPE_NORMAL).getUnits();//CombatUtils.getCombatFormation(source, true);
			for (Employee e : srcEmployees) {
				sourceTeamBuilder.addMember(e);
			}
			
			// 目标机器人
			RobotBuild _build = (RobotBuild) build;
			List<NpcTemplate> npcs = _build.getNpcs();
			for (int i = 0; i < npcs.size(); i++) {
				NpcTemplate npc = npcs.get(i);
				targetTeamBuilder.addMember(npc);
			}
		} else {
			// 发动攻击的玩家
			Employee[] srcEmployees = source.getFormations().get(Formation.TYPE_NORMAL).getUnits();//CombatUtils.getCombatFormation(source, true);
			for (Employee e : srcEmployees) {
				sourceTeamBuilder.addMember(e);
			}
			
			// 目标玩家
			long[] eids = build.currentEmployees();
			if (eids == null) eids = Formation.EMPTY_FORMATION;
			for (long eid : eids) {
				Employee e = eid != 0L ? target.getEmployees().get(eid) : null;
//				targetTeamBuilder.addEmployeeMember(eid);
				targetTeamBuilder.addMember(e);
			}
		}
		Team sourceTeam = sourceTeamBuilder.build();
		Team targetTeam = targetTeamBuilder.build();
		
		session.send(new CombatInfoResponse(packet.getSerial(), sourceTeam, targetTeam, Formation.TYPE_NORMAL));
	}
	
	private void validate(Session session, long targetPlayerId, int targetBuildId) throws CombatException {
		if (targetPlayerId == 0L || targetBuildId == 0) {
			throw new CombatException(MessageConst.ILLEGAL_OPERATION);
		}
		Player player = session.getClient();
		targetPlayerId = AdventureUtils.restoreAttackTargetId(player, targetPlayerId);
		Player target = App.getApp().getServiceManager().get(PlayerCacheService.class).find(targetPlayerId);
		if (target == null) {
			throw new CombatException(MessageConst.PLAYER_NOT_EXIST);
		}
//		Build build = target.getBuildings().getBuild(targetBuildId);
		BuildV2 build = target.getBuildings2().find(targetBuildId);
		if (build == null) {
			throw new CombatException(MessageConst.TARGET_PLAYER_BUILD_NOT_EXIST);
		}
//		build.update(); // 强制更新一下
		if (build.getState() != State.BUILT
				&& build.getState() != State.CAPTURED && build.getState() != State.ASSISTED) {
			throw new CombatException(MessageConst.BUILDING_NOW);
		}
		Player assistant = build.currentAssistant();
		if(assistant != null){
			if(player.getId() == assistant.getId()){
				throw new CombatException(MessageConst.COMBAT_PVP_CAN_NOT_ATK_ASS);
			}
		}
		Player buildOwnerNow = build.currentOwner();
		if (player.getId() == target.getId() && target.getId() == buildOwnerNow.getId()) {
			throw new CombatException(MessageConst.COMBAT_PVP_CAN_NOT_ATTACK_SELF);
		}
		if (player.getId() == buildOwnerNow.getId()) {
			throw new CombatException(MessageConst.BUILD_OCCUPIED);
		}
		this.source = player;
		this.target = buildOwnerNow;
		this.build = build;
		if (player.getId() != target.getId()) {
			if (build.getInvaderId() == 0L) {
				action = Action.INVADE;
			} else {
				action = Action.BLOCK;
			}
		} else {
			action = Action.BEAT_BACK;
		}
		if (action != Action.BEAT_BACK) {
			if (player.getConquests().isFull()) {
				throw new CombatException(MessageFormat.format(MessageConst.BUILD_OCCUPIED_EXCEED, VipUtils.getTemplate(player.getVipLvl()).getCaptureBuildMaxCount()));//GameStaticConfig.captureBuildMaxCount
			}
			RelationService rs = App.getApp().getServiceManager().get(RelationService.class);
			if (rs.isFriend(player, target) && player.getConquests().sizeByTarget(target) >= GameStaticConfig.captureSameFriendBuildMaxCount) {
				throw new CombatException(MessageFormat.format(MessageConst.BUILD_OCCUPIED_FRIEND_BUILDINGS_EXCEED, GameStaticConfig.captureSameFriendBuildMaxCount));
			}
		}
	}
	
	@Override
	public CombatResponse create(CombatService service, Type type, Session session, Packet packet) throws CombatException {
		CombatPVPRequest req = (CombatPVPRequest) packet;
		validate(session, req.getTargetId(), req.getTargetBuildId());
		
		DefaultCombatBuilder builder = new DefaultCombatBuilder();
//		builder.setSourceLeader(source);//.setDestLeader(target);
		if (RobotUtils.isRobot(target)) { // 机器人
			targetIsRobot = true;
			// 己方（队员为npc）
//			int[] npcIds = GameStaticConfig.adventureFirstCaptureSrcNpcs;
//			TemplateService ts = App.getApp().getServiceManager().get(TemplateService.class);
//			for (int id : npcIds) {
//				NpcTemplate npc = ts.get(NpcTemplate.class, id);
//				builder.addSourceMember(npc != null ? npc.toCombatabale() : null);
//			}
			Employee[] employees = source.getFormations().get(Formation.TYPE_NORMAL).getUnits();
			for (Employee e : employees) {
				builder.addSourceMember(e);
			}
			
			// 敌方机器人
			RobotBuild _build = (RobotBuild) build;
			List<NpcTemplate> npcs = _build.getNpcs();
			for (int i = 0, size = npcs.size(); i < size; i++) {
				NpcTemplate npc = npcs.get(i);
				builder.addDestMember(npc != null ? npc.toCombatabale() : null);
			}
		} else {
			// 己方
			int emptyCount = 0;
			Employee[] employees = source.getFormations().get(Formation.TYPE_NORMAL).getUnits();
			for (Employee e : employees) {
				if(e == null){
					emptyCount++;
				}
				builder.addSourceMember(e);
			}
			if(emptyCount >= 4){
				throw new CombatException(MessageConst.BUILD_CAPTURE_MUST_HAVE_EMPLOYEE);
			}
			
			// 敌方玩家
			long[] tgtEmployeeIds = build.currentEmployees();
			if (tgtEmployeeIds != null) {
				for (long eid : tgtEmployeeIds) {
					Employee e = eid != 0L ? target.getEmployees().get(eid) : null;
					builder.addDestMember(e);
				}
			}
		}
		if (source.getAdventure().getLastAction() == AdventureType.CAPTURE) {
			fromAdventure = true;
			source.getAdventure().setLastAction(null);
			source.getAdventure().setLastActionTargets(null);
		}
		Combat combat = builder.setType(Combat.Type.PVP).setListener(this).build();
		LOG.info("[PVP]COMBAT[{}]SOURCE[{}]TARGET[{}]BUILD[{}]OWNER[{}]", combat.getId(), source.getId(), target.getId(), build.getId(), build.getVm().getOwner().getId());
		return new CombatResponse(req.getSerial(), combat);
	}

	@Override
	public void end(Combat combat, boolean notify) {
		boolean succ = combat.getState() == Combat.State.SOURCE_WON;//combat.isWinner(source.getId());
		int gold = 0; // reward gold
		if (succ) {//succ
			forceRefresh(); // 重新取一遍build数据，避免目标玩家重新上线导致缓存不一致
			build.beAttacked(source);
			Player buildOwner = build.getVm().getOwner(); // 建筑原主人
			if (buildOwner.getId() != source.getId() && TimeUpdater.getInstance().now() - BuildService.lastBroadcastBuildAttackTime > BuildService.BROADCAST_BUILD_ATTACK_CD) {
				String notice = MessageFormat.format(MessageConst.BUILD_ATTACK_BROADCAST, buildOwner.getName(), build.getTemplate().getName(), source.getName());
				Server.broadcast(new NoticePush(NoticePush.NOTICE_MARQUEE, notice));
				BuildService.lastBroadcastBuildAttackTime = TimeUpdater.getInstance().now();
			}
			
//			if (buildOwner instanceof Actor) {
//				((Actor) buildOwner).setDirty(true);
//			}
			if (buildOwner.getId() != source.getId() && buildOwner.isOnline()) {
//				buildOwner.send(new CustomSync(SyncPush.SYNC_BUILD, new BuildStatusRes(buildOwner, build)));
				buildOwner.send(new BeAttackedPush(build.getId()));
			}
			if (fromAdventure) {
				source.addGold(gold = GameStaticConfig.pvpWinRewardGold, Cause.PVP_SUCC);
			}
		} else {
			if (fromAdventure) {
				source.addGold(gold = GameStaticConfig.pvpLoseRewardGold, Cause.PVP_LOSE);
			}
		}
		
		byte b = succ ? Combat.SUCC : Combat.FAIL;
		if (notify) {
			source.send(new CombatEndPush(b, Combat.Type.PVP, combat.getId(), result = new Result(build, succ, gold)));
		}
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_ATTACK_BUILD_V2, source, build.getVm().getOwner(), build, b);
		
		if (succ && action != Action.BEAT_BACK) { // 胜利且非反击战 需要等玩家操作后才能确定邮件内容
			timeout = TimeUpdater.getInstance().now() + Build.CAPTURE_OPERATE_TIMEOUT + 5000;
			callbacks.put(source.getId(), this);
		} else {
			onMsg(succ, false);
		}
		
		LOG.info("[PVP END]COMBAT[{}]SOURCE[{}]TARGET[{}]BUILD[{}]OWNER[{}]RESULT[{}]", combat.getId(), source.getId(), target.getId(), build.getId(), build.getVm().getOwner().getId(), b);
	}
	
	private void forceRefresh() {
		Player player = build.getVm().getOwner();
		player = App.getApp().getServiceManager().get(PlayerCacheService.class).find(player.getId());
		build = player.getBuildings2().find(build.getId());
	}
	
	private void onMsg(boolean succ, boolean captured) {
		if (targetIsRobot) {
			return;
		}
//		if (succ) {
//			// 计算固资变化
//			source.fireFixedAssetsChanged();
//			target = App.getApp().getServiceManager().get(ActorService.class).find(target.getId()); // force get from cache
//			target.fireFixedAssetsChanged();
//			Player buildingOwner = build.getVm().getOwner(); // 建筑初始主人
//			if (buildingOwner.getId() != target.getId() && buildingOwner.getId() != source.getId()) {
//				// 阻击战(BLOCK)
//				buildingOwner.fireFixedAssetsChanged();
//			}
//		}
		switch (action) {
			case INVADE:
				onMsgInvade(succ, captured);
				break;
			case BEAT_BACK:
				onMsgBeatBack(succ);
				break;
			case BLOCK:
				onMsgBlock(succ, captured);
				break;
		}
	}
	
	// 攻打别人
	private void onMsgInvade(boolean succ, boolean captured) {
		Date now = new Date(TimeUpdater.getInstance().now());
		String title = MessageConst.BUILD_ATTACK_MAIL_TITLE;
		
		Player oldOwner = build.getVm().getOwner();
		long buildOwnerId = oldOwner.getId();
		if(target.getId() != buildOwnerId){
			//被打的和原主人不是一家(被打的是协助者)
			// to source
//			攻击别人自己赢了：您于2016年11月7日17点15分侵占了 大傻子 的大自然氧吧，虽然 大傻子 带着员工奋力反抗，但并没什么卵用，您还是获得了112500 现金以及店面拥有权
//			攻击别人自己输了：您于2016年11月7日17点15分侵占了 大傻子 的大自然氧吧，大傻子 带着员工奋力反抗，您未能如愿以偿。
			String msg = succ ?
					MessageFormat.format(captured ? MessageConst.COMBAT_PVP_ATTACK_SUCC2 : MessageConst.COMBAT_PVP_ATTACK_SUCC, now, oldOwner.getName(), buildOwnerId, build.getTemplate().getName(), result.gold)
					: MessageFormat.format(MessageConst.COMBAT_PVP_ATTACK_FAIL, now, oldOwner.getName(), buildOwnerId, build.getTemplate().getName());
			NewsService newsService = App.getApp().getServiceManager().get(NewsService.class);
			newsService.send(source, title, msg);
			// to target
			msg = succ ?
					MessageFormat.format(captured?MessageConst.COMBAT_PVP_ASS_DEF_FAIL2:MessageConst.COMBAT_PVP_ASS_DEF_FAIL, build.getTemplate().getName(), now, source.getName(), source.getId(), result.gold, target.getName())
					: MessageFormat.format(MessageConst.COMBAT_PVP_ASS_DEF_SUCC, build.getTemplate().getName(), now, source.getName(), source.getId(), target.getName());
			newsService.send(oldOwner, title, msg);
			

			msg = succ ?
					MessageFormat.format(captured?MessageConst.COMBAT_PVP_ASS_FAIL2:MessageConst.COMBAT_PVP_ASS_FAIL, build.getTemplate().getName(), now, source.getName(), source.getId(), result.gold, oldOwner.getName())
					: MessageFormat.format(MessageConst.COMBAT_PVP_ASS_SUCC, build.getTemplate().getName(), now, source.getName(), source.getId(), oldOwner.getName());
			newsService.send(target, title, msg);
		}else{
			// to source
//			攻击别人自己赢了：您于2016年11月7日17点15分侵占了 大傻子 的大自然氧吧，虽然 大傻子 带着员工奋力反抗，但并没什么卵用，您还是获得了112500 现金以及店面拥有权
//			攻击别人自己输了：您于2016年11月7日17点15分侵占了 大傻子 的大自然氧吧，大傻子 带着员工奋力反抗，您未能如愿以偿。
			String msg = succ ?
					MessageFormat.format(captured ? MessageConst.COMBAT_PVP_ATTACK_SUCC2 : MessageConst.COMBAT_PVP_ATTACK_SUCC, now, target.getName(), target.getId(), build.getTemplate().getName(), result.gold)
					: MessageFormat.format(MessageConst.COMBAT_PVP_ATTACK_FAIL, now, target.getName(), target.getId(), build.getTemplate().getName());
			NewsService newsService = App.getApp().getServiceManager().get(NewsService.class);
			newsService.send(source, title, msg);
			// to target
//		被攻击自己输了：您的 五谷煎饼店 于2016年11月7日17点15分 被 大傻子 非法侵占，虽然您和您的员工奋力反抗，但并没什么卵用，您损失了 125000 现金以及店面的拥有权 
//		被攻击自己赢了：您的 五谷煎饼店 于2016年11月7日17点15分 被 大傻子 非法侵占，经过您和您的员工奋力反抗 大傻子 未能如愿以偿 
			msg = succ ? 
					MessageFormat.format(captured ? MessageConst.COMBAT_PVP_DEFENSE_FAIL2 : MessageConst.COMBAT_PVP_DEFENSE_FAIL, build.getTemplate().getName(), now, source.getName(), source.getId(), result.gold)
					: MessageFormat.format(MessageConst.COMBAT_PVP_DEFENSE_SUCC, build.getTemplate().getName(), now, source.getName(), source.getId());
			newsService.send(target, title, msg);
		}
	}
	
	// 夺回被别人占领建筑
	private void onMsgBeatBack(boolean succ) {
//		被人干了反击还失败：您于 2016年11月7日17点15分在被 大傻子 侵占的 五谷煎饼店 奋力反抗 大傻子 的非法行为，虽然没什么卵用，但极大的打击了他们的嚣张气焰
//		被人干了反击成功了：您于 2016年11月7日17点15分 在被 大傻子 侵占的 五谷煎饼店 前维权成功，夺回了原本属于您的财产
		Date now = new Date(TimeUpdater.getInstance().now());
		String title = MessageConst.BUILD_ATTACK_MAIL_TITLE;
		String msg = succ ?
				MessageFormat.format(MessageConst.COMBAT_PVP_BEAT_BACK_SUCC, now, target.getName(), target.getId(), build.getTemplate().getName())
				: MessageFormat.format(MessageConst.COMBAT_PVP_BEAT_BACK_FAIL, now, target.getName(), target.getId(), build.getTemplate().getName());
		NewsService newsService = App.getApp().getServiceManager().get(NewsService.class);
		newsService.send(source, title, msg);
		
		if (succ) { // 通知建筑原占有者
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BEAT_BACK_SUCC_FOR_TARGET, source.getName(), source.getId(), build.getTemplate().getName());
			newsService.send(target, title, msg);
		}
	}
	
	// 攻打别人被占领建筑
	private void onMsgBlock(boolean succ, boolean captured) {
//		别人在自己家干起来老东家赢了：二愣子 在 大傻子 非法侵占的您的 五谷煎饼店 中对 大傻子 大打出手，结果反被 大傻子 打的头破血流，撒丫子跑了。
//		别人在自己家干起来老东家输了：二愣子 在 大傻子 非法侵占的您的 五谷煎饼店 中对 大傻子 大打出手，结果 大傻子 被打蒙圈了，放弃了店面逃跑了。
		Player player = build.getVm().getOwner(); // 该建筑的原主人
		String msg = null;
		if (!succ) {
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BLOCK_FAIL, source.getName(), source.getId(), target.getName(), target.getId(), build.getTemplate().getName());
		} else if (captured) {
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BLOCK_SUCC, source.getName(), source.getId(), target.getName(), target.getId(), build.getTemplate().getName());
		}
		if (msg == null) {
			return;
		}
		String title = MessageConst.BUILD_ATTACK_MAIL_TITLE;
		NewsService newsService = App.getApp().getServiceManager().get(NewsService.class);
		newsService.send(player, title, msg);
		
		if (succ) { // 通知建筑原占有者
			msg = MessageFormat.format(MessageConst.COMBAT_PVP_BLOCK_SUCC_FOR_TARGET, player.getName(), player.getId(), build.getTemplate().getName(), source.getName(), source.getId());
			newsService.send(target, title, msg);
		}
	}
	
	class Result {
		public int gold; // 获得现金
		public long pid; // 目标玩家id
		public int bid; // 目标建筑id
		public State state;
		public byte type;
		private boolean succ;
		public Result(BuildV2 build, boolean succ, int gold) {
			this.gold = gold;
			this.pid = AdventureUtils.fakeAttackTargetId(fromAdventure ? AdventureType.CAPTURE : null, build.getVm().getOwner().getId());
			this.bid = build.getId();
			this.state = build.getState();
			this.type = (byte) (fromAdventure ? 1 : 0);
			this.succ = succ;
		}
	}

	@Override
	public void captured() {
		onMsg(result.succ, true);
	}

	@Override
	public void unCaptured() {
		onMsg(result.succ, false);
	}
	
	@Override
	public boolean isTimeout() {
		return timeout < TimeUpdater.getInstance().now();
	}
	
	public static void doCaptureCallback(Player source, boolean captured) {
		BuildCaptureCallback callback = callbacks.remove(source.getId());
		if (callback != null) {
			if (captured) {
				callback.captured();
			} else {
				callback.unCaptured();
			}
		}
	}
	
	public static void update() {
		if (callbacks.isEmpty()) {
			return;
		}
		Iterator<Entry<Long, BuildCaptureCallback>> itr = callbacks.entrySet().iterator();
		while (itr.hasNext()) {
			Entry<Long, BuildCaptureCallback> e = itr.next();
			BuildCaptureCallback callback = e.getValue();
			if (callback.isTimeout()) {
				itr.remove();
				callback.unCaptured();
			}
		}
	}

	@Override
	public void roundStart(Round round) {
	}

	@Override
	public void roundEnd(Round round) {
	}

}
